package com.jsu.service.impl;

import com.jsu.service.AgendaService;
import com.jsu.service.StreamingService;
import lombok.extern.slf4j.Slf4j;
import org.bytedeco.ffmpeg.global.avcodec;
import org.bytedeco.javacv.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;

import javax.sound.sampled.*;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.ShortBuffer;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

@Service
@Slf4j
public class StreamingServiceImpl implements StreamingService {

    private volatile boolean isStreaming = false;

    private OpenCVFrameGrabber grabber;
    private FFmpegFrameRecorder recorder;

    @Autowired
    private AgendaService agendaService;




    @Async
    public void recordWebcamAndMicrophone(int id) throws FrameGrabber.Exception, FrameRecorder.Exception {
        String liveLink = (String) agendaService.getLiveLink(id).getData();
        log.info("视频链接"+liveLink);
        isStreaming = true;
        grabber = new OpenCVFrameGrabber(700);
        grabber.setImageWidth(1000);
        grabber.setImageHeight(500);
        startGrabber(grabber);
        recorder = new FFmpegFrameRecorder(liveLink, 1000, 500, 2);
        setRecorderOptions(recorder, 35);

        startRecorder(recorder);

        captureAudio(4, recorder);

        long startTime = System.currentTimeMillis();
        long videoTS = 0;
        //开启直播

        while (isStreaming) {
            Frame capturedFrame = grabber.grabFrame();
            if (startTime == 0) {
                startTime = System.currentTimeMillis();
            }
            videoTS = 1000 * (System.currentTimeMillis() - startTime);
            if (videoTS > recorder.getTimestamp()) {
                recorder.setTimestamp(videoTS);
            }
            try {
                recorder.record(capturedFrame);
            } catch (FrameRecorder.Exception e) {
                System.out.println("录制帧发生异常，什么都不做");
            }

        }

        stopRecorder(recorder);
        stopGrabber(grabber);
    }

    @Async
    public void startGrabber(FrameGrabber grabber) throws FrameGrabber.Exception {
        try {
            grabber.start();
        } catch (FrameGrabber.Exception e) {
            grabber.restart();
        }
    }

    @Async
    public void startRecorder(FFmpegFrameRecorder recorder) throws FrameRecorder.Exception {
        try {
            recorder.start();
        } catch (FrameRecorder.Exception e) {
            recorder.stop();
            recorder.start();
        }
    }

    @Async
    public void setRecorderOptions(FFmpegFrameRecorder recorder, int frameRate) {
        recorder.setInterleaved(true);
        recorder.setVideoOption("tune", "zerolatency");
        recorder.setVideoOption("preset", "ultrafast");
        recorder.setVideoOption("crf", "25");
        recorder.setVideoBitrate(2000000);
        recorder.setVideoCodec(avcodec.AV_CODEC_ID_H264);
        recorder.setFormat("flv");
        recorder.setFrameRate(frameRate);
        recorder.setGopSize(frameRate * 2);
        recorder.setAudioOption("crf", "0");
        recorder.setAudioQuality(0);
        recorder.setAudioBitrate(192000);
        recorder.setSampleRate(44100);
        recorder.setAudioChannels(2);
        recorder.setAudioCodec(avcodec.AV_CODEC_ID_AAC);
    }

    @Async
    public void captureAudio(int audioIndex, FFmpegFrameRecorder recorder) {
        new Thread(() -> {
            AudioFormat audioFormat = new AudioFormat(44100.0F, 16, 2, true, false);
            Mixer.Info[] minfoSet = AudioSystem.getMixerInfo();
            Mixer mixer = AudioSystem.getMixer(minfoSet[audioIndex]);
            DataLine.Info dataLineInfo = new DataLine.Info(TargetDataLine.class, audioFormat);
            try {
                TargetDataLine line = (TargetDataLine) AudioSystem.getLine(dataLineInfo);
                line.open(audioFormat);
                line.start();
                int sampleRate = (int) audioFormat.getSampleRate();
                int numChannels = audioFormat.getChannels();
                int audioBufferSize = sampleRate * numChannels;
                byte[] audioBytes = new byte[audioBufferSize];
                ScheduledThreadPoolExecutor exec = new ScheduledThreadPoolExecutor(1);
                exec.scheduleAtFixedRate(() -> {
                    try {
                        int nBytesRead = line.read(audioBytes, 0, line.available());
                        int nSamplesRead = nBytesRead / 2;
                        short[] samples = new short[nSamplesRead];
                        ByteBuffer.wrap(audioBytes).order(ByteOrder.LITTLE_ENDIAN).asShortBuffer().get(samples);
                        ShortBuffer sBuff = ShortBuffer.wrap(samples, 0, nSamplesRead);
                        recorder.recordSamples(sampleRate, numChannels, sBuff);
                    } catch (FrameRecorder.Exception e) {
                        throw new RuntimeException("出现异常");
                    }
                }, 0, (long) ((long) 1000 / recorder.getFrameRate()), TimeUnit.MILLISECONDS);
            } catch (LineUnavailableException e1) {
                e1.printStackTrace();
            }
        }).start();
    }

    @Async
    public void stopGrabber(FrameGrabber grabber) {
        try {
            if (grabber != null && grabber.isDeinterlace()) {
                grabber.stop();
                grabber.release();
                System.out.println("关闭摄像头成功");
            }
        } catch (FrameGrabber.Exception e) {
            System.out.println("关闭摄像头失败");
        }
    }

    @Async
    public void stopRecorder(FFmpegFrameRecorder recorder) {
        try {
            if (recorder != null ) {
                recorder.stop();
                recorder.release();
                System.out.println("关闭记录器成功");
            }
        } catch (FrameRecorder.Exception e) {
            System.out.println("关闭录制器失败");
            try {
                recorder.stop();
                System.out.println("关闭记录器成功");
            } catch (FrameRecorder.Exception ex) {
                System.out.println("再次关闭记录器失败");
            }
        }
    }
    @Async
    @Override
    public void stopLive() {
        isStreaming = false;
        stopRecorder(recorder);
        stopGrabber(grabber);
    }

    @Async
    public void recordLive(int id){
        String liveLink = (String) agendaService.getLiveLink(id).getData();
        FFmpegFrameGrabber grabber1 = new FFmpegFrameGrabber(liveLink);//抓取直播
        FFmpegFrameRecorder recorder1 = new FFmpegFrameRecorder("D:\\javacv\\flie\\record\\"+id+".mp4",1000,500,2);
        try {
            grabber1.start();

            recorder1.setFormat("mp4");
            recorder1.setVideoCodec(avcodec.AV_CODEC_ID_H264);
            recorder1.setAudioCodec(avcodec.AV_CODEC_ID_AAC);
            recorder1.setPixelFormat(grabber1.getPixelFormat());
            recorder1.setFrameRate(grabber1.getFrameRate());

            recorder1.start();
            captureAudio(4, recorder1);


            Frame frame;
            while ((frame = grabber1.grabFrame()) != null) {
                recorder1.record(frame);
            }
        } catch (FrameGrabber.Exception | FrameRecorder.Exception e) {
            e.printStackTrace();
        } finally {
            try {
                if (recorder1 != null) {
                    recorder1.stop();
                    recorder1.release();
                }
                if (grabber1 != null) {
                    grabber1.stop();
                    grabber1.release();
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

}
